<?php

class Criteria {

    const EQUAL = "=";
    const NOT_EQUAL = "<>";
    const GREATER_THAN = ">";
    const GREATER_OR_EQUAL_THAN = ">=";
    const LESSER_THAN = "<";
    const LESSER_OR_EQUAL_THAN = "<=";
    const LIKE = "LIKE";
    const NOT_LIKE = "NOT LIKE";
    const IN = "IN";
    const NOT_IN = "NOT IN";
    const ISNULL = "IS NULL";
    const IS_NOT_NULL = "IS NOT NULL";
    const AND_EXPRESSION = "AND";
    const OR_EXPRESSION = "OR";

    /**
     * @var string
     */
    public $fieldName;

    /**
     * @var string
     */
    public $operator;

    /**
     * @var mixed
     */
    public $value;

    /**
     * @var Criteria
     */
    public $firstChildCriteria;

    /**
     * @var Criteria
     */
    public $secondChildCriteria;

    /**
     * @var string
     */
    private $criteriaConnector;

    /**
     * @var
     */
    public $criteriaIsField = false;

    /**
     * @param string|Criteria $firstOperand
     * @param string $operator
     * @param string|array|Criteria $secondOperand
     * @throws UnexpectedValueException
     */
    public function __construct($firstOperand = null, $operator = '', $secondOperand = null) {
        try {
            $this->validateConstructorParameters($firstOperand, $operator, $secondOperand);
        }
        catch(UnexpectedValueException $e) {
            throw new UnexpectedValueException($e->getMessage());
        }

        if($operator == Criteria::AND_EXPRESSION || $operator == Criteria::OR_EXPRESSION) {

            $this->firstChildCriteria = $firstOperand;
            $this->secondChildCriteria = $secondOperand;
        }
        else {

            $this->fieldName = $firstOperand;
            $this->value = $secondOperand;
        }
        $this->operator = $operator;
    }

    /**
     * @param $criteriaConnector
     */
    public function validateAndSetCriteriaConnector($criteriaConnector) {
        if($criteriaConnector == self::AND_EXPRESSION || $criteriaConnector == self::OR_EXPRESSION) {
            $this->criteriaConnector = ' ' . $criteriaConnector . ' ';
        }
        else {
            $this->criteriaConnector = '';
        }
    }

    /**
     * @return string
     */
    public function getCriteriaString() {
        switch($this->operator) {
            case self::IN:
            case self::NOT_IN:
                $criteriaString = $this->fieldName . " " . $this->operator . " ('" . implode("','", $this->value) . "') ";
                break;
            case self::ISNULL:
            case self::IS_NOT_NULL:
                $criteriaString = $this->fieldName . " " . $this->operator;
                break;
            case self::AND_EXPRESSION:
            case self::OR_EXPRESSION:
                $criteriaString = $this->firstChildCriteria->getCriteriaString() . " " . $this->operator . " " .  $this->secondChildCriteria->getCriteriaString();
                break;
            default:
                if($this->criteriaIsField) {
                    $criteriaString = $this->fieldName . " " . $this->operator . " " . $this->value;
                }
                else {
                    $criteriaString = $this->fieldName . " " . $this->operator . " '" . $this->value . "'";
                }
        }
        return $this->criteriaConnector . "(" . $criteriaString . ")";
    }

    /**
     * @param $firstOperand
     * @param $operator
     * @param $secondOperand
     * @throws UnexpectedValueException
     */
    private function validateConstructorParameters($firstOperand, $operator, $secondOperand)
    {
        $atLeastOneParameterIsEmpty = $firstOperand == NULL || $secondOperand == NULL || $operator == '';
        $firstParameterHasInvalidType = is_string($secondOperand) && is_array($secondOperand) && get_class($firstOperand) != 'Field' && get_class($firstOperand) != 'Criteria';
        $secondParameterHasInvalidType = !is_string($secondOperand) && !is_array($secondOperand) && get_class($secondOperand) != 'Criteria';
        if ($atLeastOneParameterIsEmpty || $firstParameterHasInvalidType || $secondParameterHasInvalidType) {
            throw new UnexpectedValueException('The criteria is being created with invalid values.');
        }
    }

}